import { utils } from './utils';
import { danmaku } from './danmaku';
import { position } from './models/position';
import { danmakuType } from './models/danmakuType';

export class assSubtitle {
  private readonly danmaku: danmaku;
  private readonly bottomSubtitles: Map<number, number>;
  private readonly topSubtitles: Map<number, number>;
  private readonly videoWidth: number;
  private readonly videoHeight: number;
  private readonly baseFontSize: number;
  private readonly lineCount: number;
  private readonly bottomMargin: number;
  private readonly tuneSeconds: number;
  private readonly textLength: number;
  private readonly startTime: number;
  private readonly endTime: number;
  private readonly fontSize: number;
  private readonly position: position;
  private readonly styledText: string;

  constructor(danmaku: danmaku, bottomSubtitles: Map<number, number>, topSubtitles: Map<number, number>, videoWidth: number, videoHeight: number, baseFontSize: number, lineCount: number, bottomMargin: number, tuneSeconds: number) {
    this.danmaku = danmaku;
    this.bottomSubtitles = bottomSubtitles;
    this.topSubtitles = topSubtitles;
    this.videoWidth = videoWidth;
    this.videoHeight = videoHeight;
    this.baseFontSize = baseFontSize;
    this.lineCount = lineCount;
    this.bottomMargin = bottomMargin;
    this.tuneSeconds = tuneSeconds;
    this.textLength = danmaku.content.length;
    this.startTime = danmaku.showTime;
    this.endTime = this.getEndTime(danmaku.showTime, this.tuneSeconds);
    this.fontSize = danmaku.size - 25 + baseFontSize;
    this.position = this.getPosition();
    this.styledText = this.getStyledText();
  }

  private getStyledText(): string {
    let colourMarkup = (this.danmaku.colour == 16777215) ? "" : `\\c&H${utils.toHexString(this.danmaku.colour)}`;
    let borderMarkup = (this.needWhiteBorder()) ? "\\3c&HFFFFFF" : "";
    let fontSizeMarkup = (this.fontSize == this.baseFontSize) ? "" : `\\fs${this.fontSize}`;
    let styleMarkup = (this.danmaku.type == danmakuType.normal || this.danmaku.type == danmakuType.normal2
      || this.danmaku.type == danmakuType.normal3 || this.danmaku.type == danmakuType.reverse)
      ? `\\move(${this.position.X1},${this.position.Y1},${this.position.X2},${this.position.Y2})`
      : `\\a6\\pos(${this.position.X1},${this.position.Y1})`;
    return `{{${['', styleMarkup, colourMarkup, borderMarkup, fontSizeMarkup].join('')}}}${this.danmaku.content}`;
  }

  private getPosition(): position {
    var pos = new position();
    if (this.danmaku.type == danmakuType.normal || this.danmaku.type == danmakuType.normal2 || this.danmaku.type == danmakuType.normal3) {
      pos.X1 = this.videoWidth + this.baseFontSize * this.textLength / 2;
      pos.X2 = -(this.baseFontSize * this.textLength) / 2;
      let y = (this.danmaku.index % this.lineCount + 1) * this.baseFontSize;
      if (y < this.fontSize) y = this.fontSize;
      pos.Y1 = pos.Y2 = y;
    }
    else if (this.danmaku.type == danmakuType.bottom) {
      var lineIndex = this.chooseLineCount(this.bottomSubtitles, this.startTime);
      if (this.bottomSubtitles.has(lineIndex))
        this.bottomSubtitles[lineIndex] = this.endTime;
      else
        this.bottomSubtitles.set(lineIndex, this.endTime);
      let x = this.videoWidth / 2,
        y = this.videoHeight - (this.baseFontSize * lineIndex + this.bottomMargin);
      pos.X1 = pos.X2 = x;
      pos.Y1 = pos.Y2 = y;
    }
    else if (this.danmaku.type == danmakuType.top) {
      var lineIndex = this.chooseLineCount(this.topSubtitles, this.startTime);
      if (this.topSubtitles.has(lineIndex))
        this.topSubtitles[lineIndex] = this.endTime;
      else
        this.topSubtitles.set(lineIndex, this.endTime);
      let x = this.videoWidth / 2,
        y = this.baseFontSize * lineIndex + 1;
      pos.X1 = pos.X2 = x;
      pos.Y1 = pos.Y2 = y;
    }
    else if (this.danmaku.type == danmakuType.reverse) {
      pos.X2 = this.videoWidth + this.baseFontSize * this.textLength / 2;
      pos.X1 = -(this.baseFontSize * this.textLength) / 2;
      let y = (this.danmaku.index % this.lineCount + 1) * this.baseFontSize;
      if (y < this.fontSize) y = this.fontSize;
      pos.Y1 = pos.Y2 = y;
    }
    return pos;
  }

  private chooseLineCount(subtitles: Map<number, number>, startTime: number): number {
    let lineIndex;
    let toRemove = new Array<number>();
    for (let item of subtitles) {
      if (item[1] <= startTime)
        toRemove.push(item[0]);
    }
    toRemove.forEach(s => subtitles.delete(s));
    if (subtitles.size == 0)
      lineIndex = 0;
    else if (subtitles.size == Math.max(...subtitles.keys()))
      lineIndex = Math.min(...subtitles.keys());
    else {
      lineIndex = 0;
      for (let i = 0; i < subtitles.size; i++) {
        if (!subtitles.has(i)) {
          lineIndex = i;
          break;
        }
      }
      if (lineIndex == 0)
        lineIndex = subtitles.size;
    }
    return lineIndex;
  }

  private getEndTime(showTime: number, offset: number): number {
    if (this.danmaku.type == danmakuType.bottom || this.danmaku.type == danmakuType.top)
      return showTime + 4;
    let endTime;
    if (this.textLength < 5) {
      endTime = showTime + 7 + this.textLength / 1.5;
    } else if (this.textLength < 12) {
      endTime = showTime + 7 + this.textLength / 2;
    } else {
      endTime = showTime + 13;
    }
    endTime += offset;
    return endTime;
  }

  private needWhiteBorder(): boolean {
    var colour = this.danmaku.colour;
    if (colour == 0) return true;
    var hls = utils.toHls(this.danmaku.colour);
    if (hls.H > 30 && hls.H < 210 && hls.L < 33) return true;
    if ((hls.L < 30 || hls.H > 210) && hls.L < 66) return true;
    return false;
  }
}